package ast

import (
	"fmt"
	"strconv"
	"strings"
	"sync/atomic"

	"gitee.com/u-language/u-language/ucom/data"
	"gitee.com/u-language/u-language/ucom/errcode"
	"gitee.com/u-language/u-language/ucom/internal/errutil"
	"gitee.com/u-language/u-language/ucom/internal/utils"
	"gitee.com/u-language/u-language/ucom/lex"
)

var _ GetImport = (*Package)(nil)
var _ data.IsItARecursiveType = (*Package)(nil)

// Package 管理一个包的所有源代码文件的抽象语法树
type Package struct {
	//导入表
	ImportTable *Sbt
	//错误处理上下文
	errctx      *errcode.ErrCtx
	PackageName atomic.Pointer[string]
	//在自动释放块被调用的函数名
	InAutoFreeFuncCallName data.RemoveDupStrck[FuncNameNode]
	//符号表
	Sbt *Sbt
	//泛型表
	GenericTable *Sbt
	//泛型实例化节点
	GenInstNodes *Sbt
	//自己及依赖导入的包
	ImportPackage *map[string]*Package
	Dir           string
	//自己导入的包
	ImportLoacl []data.IsItARecursiveType
	//变量初始化
	VarInitTable_Global VarInitTable
	//保存抽象语法树
	Trees data.Slice[*Tree]
	//是否有init函数
	IsInitFunc atomic.Bool
	//是否有自动释放块
	IsHaveAutoFree atomic.Bool
	//C代码的头文件包含的节点
	CHeaderFile data.Slice[CHeaderFile]
	//并发
	Thread bool
}

// NewPackage 创建管理一个包的所有源代码文件的抽象语法树的 [Package]
//   - thread是控制是否并发
//   - errctx是错误处理上下文
func NewPackage(dir string, thread bool, errctx *errcode.ErrCtx, sbt *Sbt, ImportPackage *map[string]*Package) *Package {
	ret := &Package{Thread: thread,
		errctx:                 errctx,
		Trees:                  data.Slice[*Tree]{Thread: thread},
		Dir:                    dir,
		Sbt:                    sbt,
		ImportTable:            NewSbt(thread),
		InAutoFreeFuncCallName: data.NewRemoveDupStrck[FuncNameNode](thread),
		GenericTable:           NewSbt(thread),
		GenInstNodes:           NewSbt(thread),
		VarInitTable_Global:    NewVarInitTable(thread),
		ImportPackage:          ImportPackage,
		CHeaderFile:            data.Slice[CHeaderFile]{Thread: thread},
	}
	ret.Sbt.importPackage, ret.GenericTable.isUseCount = ret.ImportPackage, true
	return ret
}

// AddFile 将一个源代码文件的词法分析结果转换为抽象语法树保存
//   - ft 是源代码文件的词法分析结果
func (p *Package) AddFile(ft *lex.FileToken) {
	if ft == nil {
		return
	}
	tree := newTree(p.Sbt, p.Thread, ft.File, p.errctx, &p.IsInitFunc, &p.InAutoFreeFuncCallName, p.ImportTable, p.ImportPackage, &p.ImportLoacl, &p.IsHaveAutoFree, p.GenericTable, p.GenInstNodes, &p.VarInitTable_Global, &p.CHeaderFile)
	ParserNode(tree, ft.Value, tree.Sbt, ParserNodeState{Index: 0, End: 0, Ret: nil, FuncInfo: nil, IsRecursion: false, InAutoFree: false})
	tree.errctx = nil //当词法分析结果被转换为抽象语法树后，错误处理上下文不需要了，不需要保留指针
	//如果不同文件声明的包声明不同
	if p.PackageName.Load() == nil || *p.PackageName.Load() == "" {
		p.PackageName.Store(&tree.PackageName)
	} else if *p.PackageName.Load() != tree.PackageName {
		p.errctx.Panic(p.Dir, 0, errcode.NewMsgDiffFileHavaDiffPackageDecl(*p.PackageName.Load(), tree.PackageName), errcode.DiffFileHavaDiffPackageDecl)
	}
	p.Trees.Add(tree)
}

// FindTree 寻找自己及依赖中是否有 file 的抽象语法树
func (p *Package) FindTree(file string) *Tree {
	for _, v := range p.Trees.Data {
		if v.Filename == file {
			return v
		}
	}
	for _, v := range *p.ImportPackage {
		for _, v := range v.Trees.Data {
			if v.Filename == file {
				return v
			}
		}
	}
	panic(errutil.NewErr2(errutil.CompileErr, fmt.Sprintf("未找到 %s 的抽象语法树", file)))
}

func (p *Package) Parser() {
	generateGenInst(p.Trees.Data[0], p.FindTree)
}

func (p *Package) GetImportTable() *Sbt {
	return p.ImportTable
}

func (p *Package) GerImportPackage() *map[string]*Package {
	return p.ImportPackage
}

func (p *Package) My() string {
	return *p.PackageName.Load()
}

func (p *Package) DepLen() int {
	return len(p.ImportLoacl)
}

func (p *Package) Dep(i int) data.IsItARecursiveType {
	return p.ImportLoacl[i]
}

func (p *Package) GetImportLoacl() *[]data.IsItARecursiveType {
	return &p.ImportLoacl
}

func (p *Package) String() string {
	var buf strings.Builder
	buf.WriteString("package: ")
	buf.WriteString(*p.PackageName.Load())
	buf.WriteString("\n")
	for _, v := range p.Trees.Data {
		buf.WriteString(v.Filename)
		buf.WriteString("\n\n")
		for i, n := range v.Nodes {
			if utils.IsNil(n) {
				continue
			}
			buf.WriteString(strconv.Itoa(i))
			buf.WriteString(n.String())
			buf.WriteString("\n")
		}
	}
	buf.WriteString(p.Sbt.String())
	return buf.String()
}
